package jp.tkym.labs.proc;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
	
public class PreparedJavaw {
	private static final PreparedJavaw singleton = new PreparedJavaw();
	private PreparedJavaw(){}
	static PreparedJavaw getInstance(){
		return singleton;
	}
	public static final DefaultJavaProcessWorkerFacotry DEFAULT_FACTORY = new DefaultJavaProcessWorkerFacotry();
	private PreparedJavaProcessPool pool = null;
	
	public PreparedJavawAdmin admin(){
		return new PreparedJavawAdmin(this);
	}
	
	public class PreparedJavawAdmin{
		private PreparedJavaw prepared;
		private List<String> taskClass = new ArrayList<String>();
		private int poolSize = 1;
		private JavaProcessWorkerFactory factory = DEFAULT_FACTORY;
		PreparedJavawAdmin(PreparedJavaw prepared){
			this.prepared = prepared;
		}
		public void startup() throws Exception{
			prepared.pool = new PreparedJavaProcessPool(poolSize, factory, taskClass);
			prepared.pool().setup();
		}
		public void shutdown() throws Exception{
			prepared.pool().teardown();
		}
		public <T extends Runnable> PreparedJavawAdmin addPrepareTask(Class<T> clazz){
			addPrepareTask(clazz.getName());
			return this;
		}
		public <T extends Runnable> PreparedJavawAdmin removePrepareTask(Class<T> clazz){
			removePrepareTask(clazz.getName());
			return this;
		}
		public <T extends Runnable> PreparedJavawAdmin addPrepareTask(String className){
			taskClass.add(className);
			return this;
		}
		public <T extends Runnable> PreparedJavawAdmin removePrepareTask(String className){
			taskClass.remove(className);
			return this;
		}
		public PreparedJavawAdmin setPrecallProcessFactory(JavaProcessWorkerFactory f){
			factory = f;
			return this;
		}
	}
	
	public PrecallProcessLounch launch() throws Exception{
		return new PrecallProcessLounch(pool().getProcess());
	}
	
	public class PrecallProcessLounch extends AbstractJavaProcessLounch{
		private PreparedJavaProcess waiting;
		PrecallProcessLounch(PreparedJavaProcess waiting){
			this.waiting = waiting;
		}
		
		@Override
		public JavaProcessWorker main(Class<?> main) throws Exception {
			return main(main.getName());
		}
	
		public JavaProcessWorker main(String classname) throws Exception {
			return this.waiting.restart(classname, option());
		}
	}
	
	private PreparedJavaProcessPool pool(){
		if(pool == null) pool = createDefaultPool();
		return pool;
	}
	
	private PreparedJavaProcessPool createDefaultPool(){
		return new PreparedJavaProcessPool(1, DEFAULT_FACTORY, new ArrayList<String>());
	}
	
	class PreparedJavaProcessPool {
		private Queue<PreparedJavaProcess> queue = new LinkedList<PreparedJavaProcess>();
		private PreparedJavaProcessBuilder builder;
		private int size;
		
		PreparedJavaProcessPool(int size, JavaProcessWorkerFactory factory, List<String> classNameOfPretask){
			this.size = size;
			this.builder = new PreparedJavaProcessBuilder(factory, classNameOfPretask);
		}
		
		void setup() throws Exception{
			for(int i=0; i<size; i++) 
				queue.offer(builder.build());
		}
		
		void teardown() throws Exception{
			PreparedJavaProcess precall = queue.poll();
			while(precall != null){
				precall.exit();
				precall = queue.poll();
			}
		}
		
		private PreparedJavaProcess getProcess() throws Exception{
			queue.offer(builder.build());
			return queue.poll();
		}
	}
	
	private class PreparedJavaProcessBuilder{
		private JavaProcessWorkerFactory factory;
		private List<String> classNameOfPretask;
		PreparedJavaProcessBuilder(JavaProcessWorkerFactory factory, List<String> classNameOfPretask){
			this.factory = factory;
			this.classNameOfPretask = classNameOfPretask;
		}
		PreparedJavaProcess build() throws Exception{
			PreparedJavaProcess prepared = new PreparedJavaProcess(factory.create());
			for(String runnable : classNameOfPretask) prepared.precall(runnable);
			return prepared;
		}
	}
	
	private static class DefaultJavaProcessWorkerFacotry implements JavaProcessWorkerFactory{
		@Override
		public JavaProcessWorker create() throws Exception{
			return Javaw.create().main(PROXY_BOOTING);
		}
	}
	
	public interface JavaProcessWorkerFactory{
		public static final Class<?> PROXY_BOOTING = PreparedBooting.class;
		JavaProcessWorker create() throws Exception;
	}
}